/*
 * Copyright (c) 1991-1994 by Xerox Corporation.  All rights reserved.
 * Copyright (c) 2001 by Hewlett-Packard Company. All rights reserved.
 * Copyright (c) 2009-2025 Ivan Maidanski
 *
 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
 * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
 *
 * Permission is hereby granted to use or copy this program
 * for any purpose, provided the above notices are retained on all copies.
 * Permission to modify the code and to distribute modified code is granted,
 * provided the above notices are retained, and a notice that the code was
 * modified is included with the above copyright notice.
 */

/*
 * This contains interfaces to the garbage collector marker that are likely
 * to be useful to clients that provide detailed heap layout information
 * to the collector.  This interface should not be used by normal C or C++
 * clients.  It will be useful to runtimes for other languages.
 *
 * This is an experts-only interface!  There are many ways to break the
 * collector in subtle ways by using this functionality.
 */
#ifndef GC_MARK_H
#define GC_MARK_H

#ifndef GC_H
#  include "gc.h"
#endif

#ifdef __cplusplus
extern "C" {
#endif

#define GC_PROC_BYTES 100

#if defined(GC_BUILD) || defined(NOT_GCBUILD)
struct GC_ms_entry;
struct GC_hblk_s;
#else
struct GC_ms_entry {
  void *opaque;
};
struct GC_hblk_s {
  void *opaque;
};
#endif

/**
 * A client-supplied mark procedure.  Returns new mark stack pointer.
 * Primary effect should be to push new entries on the mark stack.
 * Mark stack pointer values are passed and returned explicitly.
 * Global variables describing mark stack are not necessarily valid.
 * (This usually saves a few cycles by keeping things in registers.)
 * Assumed to scan about `GC_PROC_BYTES` on average.  If it needs to do
 * much more work than that, it should do it in smaller pieces by
 * pushing itself back on the mark stack.  Note that it should always
 * do some work (defined as "marking some objects") before pushing more
 * than one entry on the mark stack.  This is required to ensure
 * termination in the event of mark stack overflows.  This procedure is
 * always called with at least one empty entry on the mark stack.
 * Currently we require that mark procedures look for pointers in
 * a subset of the places the conservative marker would.  It must be safe
 * to invoke the normal mark procedure instead.
 *
 * *Warning*: Such a mark procedure may be invoked on an unused object
 * residing on a free list.  Such objects are cleared, except for a
 * free-list link field (which is located at the beginning of each
 * object).  Thus mark procedures may not count on the presence of a
 * type descriptor, and must handle this case correctly somehow.  Also,
 * a mark procedure should be prepared to be executed concurrently from
 * the marker threads (the latter ones are created only if the client
 * has called `GC_start_mark_threads()` or started a user thread
 * previously).  For the compatibility reason, `addr` is a pointer to
 * `GC_word`, but it should be treated as a pointer to `void` pointer.
 */
typedef struct GC_ms_entry *(GC_CALLBACK *GC_mark_proc)(
    GC_word * /* `addr` */, struct GC_ms_entry * /* `mark_stack_top` */,
    struct GC_ms_entry * /* `mark_stack_limit` */, GC_word /* `env` */);

#define GC_LOG_MAX_MARK_PROCS 6
#define GC_MAX_MARK_PROCS (1 << GC_LOG_MAX_MARK_PROCS)

/*
 * In a few cases it is necessary to assign statically known indices to
 * certain mark procedures.  Thus we reserve a few for well known clients.
 * (This is necessary if mark descriptors are compiler generated.)
 */
#define GC_RESERVED_MARK_PROCS 8
#define GC_GCJ_RESERVED_MARK_PROC_INDEX 0

/*
 * Object descriptors on mark stack or in objects.  Low-order two bits are
 * tags distinguishing among the following 4 possibilities for the rest
 * (high-order) bits.
 */
#define GC_DS_TAG_BITS 2
#define GC_DS_TAGS ((1U << GC_DS_TAG_BITS) - 1)

/**
 * The entire descriptor is a length in bytes that must be a multiple of 4.
 */
#define GC_DS_LENGTH 0

/**
 * The high-order bits are describing pointer fields.  The most
 * significant bit is set if the first "pointer-sized" word is a pointer.
 * (This unconventional ordering sometimes makes the marker slightly faster.)
 * Zeros indicate definite non-pointers; ones indicate possible pointers.
 * *Note*: only usable if pointers are aligned on the size of a pointer (thus,
 * extra care should be taken by client on cris and m68k architectures).
 */
#define GC_DS_BITMAP 1

/**
 * The objects referenced by this object can be pushed on the mark stack by
 * invoking `PROC(descr)`.  `ENV(descr)` is passed as the last argument.
 */
#define GC_DS_PROC 2

/**
 * The real descriptor is at the byte displacement from the beginning
 * of the object given by `descr & ~GC_DS_TAGS`.  If the descriptor is
 * negative, the real descriptor is at
 * `*<object_start> - (descr & ~GC_DS_TAGS) - GC_INDIR_PER_OBJ_BIAS`.
 * The latter alternative can be used if each object contains a type
 * descriptor at the beginning of the object.  Note that in the
 * multi-threaded environments per-object descriptors must be located
 * in either the first two or last two "pointer-sized" words of the
 * object, since only those are guaranteed to be cleared while the
 * allocator lock is held.
 */
#define GC_DS_PER_OBJECT 3

#define GC_INDIR_PER_OBJ_BIAS 0x10

#define GC_MAKE_PROC(proc_index, env)                                      \
  ((((((GC_word)(env)) << GC_LOG_MAX_MARK_PROCS) | (unsigned)(proc_index)) \
    << GC_DS_TAG_BITS)                                                     \
   | (GC_word)GC_DS_PROC)

/**
 * Bounds on the heap.  Guaranteed to be valid.  Likely to include future
 * heap expansion.  Hence the bounded range usually includes not-yet-mapped
 * memory, or might overlap with other data roots.  The address of any heap
 * object is larger than `GC_least_plausible_heap_addr` and less than
 * `GC_greatest_plausible_heap_addr`.
 */
GC_API void *GC_least_plausible_heap_addr;
GC_API void *GC_greatest_plausible_heap_addr;

/**
 * Specify the pointer address mask.  Works only if the collector is
 * built with `DYNAMIC_POINTER_MASK` macro defined.  These primitives
 * are normally needed only to support systems that use high-order
 * pointer tags.  The setter is expected to be called, if needed,
 * before the collector initialization or, at least, before the first
 * object is allocated.  Both the setter and the getter are unsynchronized.
 */
GC_API void GC_CALL GC_set_pointer_mask(GC_word);
GC_API GC_word GC_CALL GC_get_pointer_mask(void);

/**
 * Similar to `GC_set_pointer_mask`/`GC_get_pointer_mask` but for the
 * pointer address shift.  The value should be less than the size of
 * `GC_word`, in bits.  Applied after the mask.
 */
GC_API void GC_CALL GC_set_pointer_shift(unsigned);
GC_API unsigned GC_CALL GC_get_pointer_shift(void);

/**
 * Handle nested references in a custom mark procedure.
 * Check if `obj` is a valid object.  If so, ensure that it is marked.
 * If it was not previously marked, push its contents onto the mark
 * stack for future scanning.  The object will then be scanned using
 * its mark descriptor.  Returns the new mark stack pointer.
 * Handles mark stack overflows correctly.  Since this marks first, it
 * makes progress even if there are mark stack overflows.
 * `src` is the address of the pointer to `obj`, which is used only
 * for back pointer-based heap debugging.
 * It is strongly recommended that most objects be handled without mark
 * procedures, e.g. with bitmap descriptors, and that mark procedures
 * be reserved for the exceptional cases.  That will ensure that
 * performance of this call is not extremely performance critical.
 * (Otherwise we would need to inline `GC_mark_and_push` completely,
 * which would tie the client code to a fixed collector version.)
 * Note that mark procedures should explicitly call `FIXUP_POINTER()`
 * if required.
 */
GC_API struct GC_ms_entry *GC_CALL GC_mark_and_push(
    void * /* `obj` */, struct GC_ms_entry * /* `mark_stack_top` */,
    struct GC_ms_entry * /* `mark_stack_limit` */, void ** /* `src` */);

#define GC_MARK_AND_PUSH(obj, msp, lim, src)                       \
  (GC_ADDR_LT((char *)GC_least_plausible_heap_addr, (char *)(obj)) \
           && GC_ADDR_LT((char *)(obj),                            \
                         (char *)GC_greatest_plausible_heap_addr)  \
       ? GC_mark_and_push(obj, msp, lim, src)                      \
       : (msp))

GC_API void GC_CALL GC_push_proc(GC_word /* `descr` */, void * /* `obj` */);

GC_API struct GC_ms_entry *GC_CALL
GC_custom_push_proc(GC_word /* `descr` */, void * /* `obj` */,
                    struct GC_ms_entry * /* `mark_stack_top` */,
                    struct GC_ms_entry * /* `mark_stack_limit` */);

GC_API struct GC_ms_entry *GC_CALL
GC_custom_push_range(void * /* `bottom` */, void * /* `top` */,
                     struct GC_ms_entry * /* `mark_stack_top` */,
                     struct GC_ms_entry * /* `mark_stack_limit` */);

/**
 * The size of the header added to objects allocated through the `GC_debug_`
 * routines.  Defined as a function so that client mark procedures do not
 * need to be recompiled for the collector library version changes.
 */
GC_API GC_ATTR_CONST size_t GC_CALL GC_get_debug_header_size(void);
#define GC_USR_PTR_FROM_BASE(p) \
  ((void *)((char *)(p) + GC_get_debug_header_size()))

/*
 * The same as `GC_get_debug_header_size()` but defined as a variable.
 * Exists only for the backward compatibility.  Some compilers do not
 * accept `const` together with `deprecated` or `dllimport` attributes,
 * so the symbol is exported as a non-constant one.
 */
GC_API GC_ATTR_DEPRECATED
#ifdef GC_BUILD
    const
#endif
        size_t GC_debug_header_size;

/**
 * Return the heap block size.  Each heap block is devoted to a single size
 * and kind of object.
 */
GC_API GC_ATTR_CONST size_t GC_CALL GC_get_hblk_size(void);

typedef void(GC_CALLBACK *GC_walk_hblk_fn)(struct GC_hblk_s *,
                                           void * /* `client_data` */);

/**
 * Apply `fn` to each allocated heap block.  It is the responsibility
 * of the caller to avoid data race during the function execution
 * (e.g. by acquiring the allocator lock at least in the reader mode).
 */
GC_API void GC_CALL GC_apply_to_all_blocks(GC_walk_hblk_fn,
                                           void * /* `client_data` */)
    GC_ATTR_NONNULL(1);

/* Same as `GC_walk_hblk_fn` but with index of the free list. */
typedef void(GC_CALLBACK *GC_walk_free_blk_fn)(struct GC_hblk_s *,
                                               int /* `index` */,
                                               void * /* `client_data` */);

/**
 * Apply `fn` to each completely empty heap block.  It is the responsibility
 * of the caller to avoid data race during the function execution (e.g. by
 * acquiring the allocator lock at least in the reader mode).
 */
GC_API void GC_CALL GC_iterate_free_hblks(GC_walk_free_blk_fn,
                                          void * /* `client_data` */)
    GC_ATTR_NONNULL(1);

/**
 * If there are likely to be false references to a block starting at
 * `h` of the indicated length (`len`), then return the next plausible
 * starting location for `h` that might avoid these false references.
 * (In other words: Is the block starting at `h` of size `len` bytes
 * black-listed?  If so, then return the address of the next plausible
 * `r` such that (`r`,`len`) might not be black-listed.  Note that
 * pointer `r` may not actually be in the heap, we guarantee only that
 * every smaller value of `r` after `h` is also black-listed.)
 * Otherwise `NULL` is returned.  Assumes the allocator lock is held at
 * least in the reader mode, but no assertion about it by design.
 */
GC_API struct GC_hblk_s *GC_CALL
GC_is_black_listed(struct GC_hblk_s * /* `h` */, size_t /* `len` */);

/**
 * Return the number of set mark bits for the heap block where object `p`
 * is located.  Defined only if the library has been compiled without
 * `NO_DEBUGGING` macro defined.
 */
GC_API unsigned GC_CALL GC_count_set_marks_in_hblk(const void * /* `p` */);

/*
 * And some routines to support creation of new "kinds", e.g. with custom
 * mark procedures, by language runtimes.  The `_inner` variants assume
 * the caller holds the allocator lock.
 */

/** Return a new free-list array. */
GC_API void **GC_CALL GC_new_free_list(void);
GC_API void **GC_CALL GC_new_free_list_inner(void);

/**
 * Return a new kind, as specified.  The last two parameters must be zero
 * or one.
 */
GC_API unsigned GC_CALL GC_new_kind(void ** /* `free_list` */,
                                    GC_word /* `mark_descriptor_template` */,
                                    int /* `add_size_to_descriptor` */,
                                    int /* `clear_new_objects` */)
    GC_ATTR_NONNULL(1);
GC_API unsigned GC_CALL GC_new_kind_inner(
    void ** /* `free_list` */, GC_word /* `mark_descriptor_template` */,
    int /* `add_size_to_descriptor` */, int /* `clear_new_objects` */)
    GC_ATTR_NONNULL(1);

/**
 * Return a new mark procedure identifier, suitable for use as the first
 * argument in `GC_MAKE_PROC()`.
 */
GC_API unsigned GC_CALL GC_new_proc(GC_mark_proc);
GC_API unsigned GC_CALL GC_new_proc_inner(GC_mark_proc);

/**
 * Similar to `GC_init_gcj_malloc()` described in `gc_gcj.h` file but with
 * the proper types of the arguments and an additional runtime checking.
 * `GC_GCJ_MARK_DESCR_OFFSET` should be passed to `descr_offset` argument.
 * Defined only if the library has been compiled with `GC_GCJ_SUPPORT`
 * macro defined.
 */
GC_API void GC_CALL GC_init_gcj_malloc_mp(unsigned /* `mp_index` */,
                                          GC_mark_proc /* `mp` */,
                                          size_t /* `descr_offset` */);

/**
 * Allocate an object of a given `kind`.  By default, there are only a few
 * kinds: composite (pointer-containing), atomic, uncollectible, etc.
 * We claim it is possible for clever client code that understands the
 * collector internals to add more, e.g. to communicate object layout
 * information to the collector.  Note that in the multi-threaded
 * contexts, this is usually unsafe for kinds that have the descriptor
 * in the object itself, since there is otherwise a window in which
 * the descriptor is not correct.  Even in the single-threaded case,
 * we need to be sure that cleared objects on a free list do not cause
 * a GC crash if they are accidentally traced.
 */
GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void *GC_CALL
    GC_generic_malloc(size_t /* `lb` */, int /* `kind` */);
GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void *GC_CALL
    GC_debug_generic_malloc(size_t /* `lb` */, int /* `kind` */,
                            GC_EXTRA_PARAMS);

/**
 * Same as `GC_generic_malloc()`, but pointers to past the first heap block
 * of the resulting object are ignored.  We avoid holding the allocator lock
 * while we clear the memory.
 */
GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void *GC_CALL
    GC_generic_malloc_ignore_off_page(size_t /* `lb` */, int /* `kind` */);

/**
 * A generalized variant of `GC_malloc_uncollectable()` and
 * `GC_malloc_atomic_uncollectable()`.
 */
GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void *GC_CALL
    GC_generic_malloc_uncollectable(size_t /* `lb` */, int /* `kind` */);

/**
 * Same as `GC_generic_malloc()`, but primary for allocating an object
 * of the same kind as an existing one (`kind` obtained by
 * `GC_get_kind_and_size()`).  Not suitable for `gcj` and typed-`malloc`
 * kinds.
 */
GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void *GC_CALL
    GC_generic_or_special_malloc(size_t /* `lb` */, int /* `kind` */);
GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void *GC_CALL
    GC_debug_generic_or_special_malloc(size_t /* `lb` */, int /* `kind` */,
                                       GC_EXTRA_PARAMS);

#ifdef GC_DEBUG
#  define GC_GENERIC_MALLOC(sz, k) GC_debug_generic_malloc(sz, k, GC_EXTRAS)
#  define GC_GENERIC_OR_SPECIAL_MALLOC(sz, k) \
    GC_debug_generic_or_special_malloc(sz, k, GC_EXTRAS)
#else
#  define GC_GENERIC_MALLOC(sz, k) GC_generic_malloc(sz, k)
#  define GC_GENERIC_OR_SPECIAL_MALLOC(sz, k) \
    GC_generic_or_special_malloc(sz, k)
#endif

/**
 * Similar to `GC_size` but returns object kind.  The size is returned too
 * if `psize` is not `NULL`.  The object pointer should be non-`NULL`.
 */
GC_API int GC_CALL GC_get_kind_and_size(const void *, size_t * /* `psize` */)
    GC_ATTR_NONNULL(1);

/**
 * A procedure which produces a human-readable description of the
 * "type" of object `p` into the buffer `out_buf` of length
 * `GC_TYPE_DESCR_LEN`.  This is used by the debug support when
 * printing objects.  These functions should be as robust as possible,
 * though we do avoid invoking them on objects on the global free list.
 */
typedef void(GC_CALLBACK *GC_describe_type_fn)(void * /* `p` */,
                                               char * /* `out_buf` */);
#define GC_TYPE_DESCR_LEN 40

/**
 * Register a `describe_type` function to be used when printing objects
 * of a particular `kind`.
 */
GC_API void GC_CALL GC_register_describe_type_fn(int /* `kind` */,
                                                 GC_describe_type_fn);

/**
 * Clear some of the inaccessible part of the stack.  Returns its argument,
 * so it can be used in a tail call position, hence clearing another frame.
 * The argument may be `NULL`.
 */
GC_API void *GC_CALL GC_clear_stack(void *);

/**
 * Set/get the client notifier on collections.  The client-supplied procedure
 * is called at the start of every full collection (called with the allocator
 * lock held).  May be 0.  This is a really tricky interface to use correctly.
 * Unless you really understand the collector internals, the callback should
 * not, directly or indirectly, make any `GC_` or potentially blocking calls.
 * In particular, it is not safe to allocate memory using the collector from
 * within the callback procedure.  Both the setter and the getter acquire the
 * allocator lock (in the reader mode in case of the getter).
 */
typedef void(GC_CALLBACK *GC_start_callback_proc)(void);
GC_API void GC_CALL GC_set_start_callback(GC_start_callback_proc);
GC_API GC_start_callback_proc GC_CALL GC_get_start_callback(void);

/**
 * Slow/general mark bit manipulation.  The caller should hold the
 * allocator lock.  The argument should be the real address of an object
 * (i.e. the address of the debug header if there is one).
 */
GC_API void GC_CALL GC_clear_mark_bit(const void *) GC_ATTR_NONNULL(1);
GC_API void GC_CALL GC_set_mark_bit(const void *) GC_ATTR_NONNULL(1);

/**
 * Get the mark bit.  Returns 1 (true) or 0.  The caller should hold the
 * allocator lock at least in the reader mode.  The argument should be
 * the real address of an object (i.e. the address of the debug header
 * if there is one).
 */
GC_API int GC_CALL GC_is_marked(const void *) GC_ATTR_NONNULL(1);

/**
 * Push everything in the given range onto the mark stack.  `bottom` is
 * the first location to be scanned; `top` is one past the last location
 * to be scanned.  Should only be used if there is no possibility of mark
 * stack overflow.
 */
GC_API void GC_CALL GC_push_all(void * /* `bottom` */, void * /* `top` */);

/**
 * Similar to `GC_push_all` but treats all interior pointers as valid and
 * scans the entire region immediately (not just schedules it for scanning),
 * in case the contents change.
 */
GC_API void GC_CALL GC_push_all_eager(void * /* `bottom` */,
                                      void * /* `top` */);

/**
 * Similar to `GC_push_all` but processes either all or only dirty pages
 * depending on `all` argument.
 */
GC_API void GC_CALL GC_push_conditional(void * /* `bottom` */,
                                        void * /* `top` */,
                                        int /* `bool` `all` */);

GC_API void GC_CALL GC_push_finalizer_structures(void);

/**
 * Set/get the client push-other-roots procedure.  A client-supplied
 * procedure should also call the original one.  Note that both the setter
 * and the getter require some external synchronization to avoid data race.
 */
typedef void(GC_CALLBACK *GC_push_other_roots_proc)(void);
GC_API void GC_CALL GC_set_push_other_roots(GC_push_other_roots_proc);
GC_API GC_push_other_roots_proc GC_CALL GC_get_push_other_roots(void);

/**
 * Walk the GC heap visiting all reachable objects.  Assume the caller
 * holds the allocator lock at least in the reader mode.  Object base
 * pointer, object size and client custom data are passed to the callback
 * (holding the allocator lock in the same mode as the caller does).
 */
typedef void(GC_CALLBACK *GC_reachable_object_proc)(
    void * /* `obj` */, size_t /* `bytes` */, void * /* `client_data` */);
GC_API void GC_CALL GC_enumerate_reachable_objects_inner(
    GC_reachable_object_proc, void * /* `client_data` */) GC_ATTR_NONNULL(1);

/**
 * Is the given address in one of the temporary static root sections?
 * Acquires the allocator lock in the reader mode.  For the debugging
 * purpose only.
 */
GC_API int GC_CALL GC_is_tmp_root(void *);

GC_API void GC_CALL GC_print_trace(GC_word /* `gc_no` */);
GC_API void GC_CALL GC_print_trace_inner(GC_word /* `gc_no` */);

/**
 * Set the client for when mark stack is empty.  A client can use this
 * callback to process (un)marked objects and push additional work onto
 * the stack.  Useful for implementing ephemerons.  Both the setter and
 * the getter acquire the allocator lock (in the reader mode in case of
 * the getter).
 */
typedef struct GC_ms_entry *(GC_CALLBACK *GC_on_mark_stack_empty_proc)(
    struct GC_ms_entry * /* `mark_stack_top` */,
    struct GC_ms_entry * /* `mark_stack_limit` */);
GC_API void GC_CALL GC_set_on_mark_stack_empty(GC_on_mark_stack_empty_proc);
GC_API GC_on_mark_stack_empty_proc GC_CALL GC_get_on_mark_stack_empty(void);

#ifdef __cplusplus
} /* extern "C" */
#endif

#endif /* GC_MARK_H */
